#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

struct private_data {
    int *gpios;
    int *values;
    int gpios_num;
    struct platform_device *pdev;
    struct timer_list timer;
};

static void rk3588_gpio_test_timer_callback(struct timer_list *timer)
{
    struct private_data *p = container_of(timer, struct private_data, timer);
    int i;
    for (i = 0; i < p->gpios_num; ++i) {
        p->values[i] = p->values[i] ? 0 : 1;
        gpio_set_value(p->gpios[i], p->values[i]);
        dev_info(&p->pdev->dev, "----------------------------------\n");
        dev_info(&p->pdev->dev, "value[%d]=%d\n", i, p->values[i]);
        dev_info(&p->pdev->dev, "----------------------------------\n");
    }
    mod_timer(timer, jiffies + msecs_to_jiffies(500));
}

static int rk3588_gpio_test_probe(struct platform_device *pdev)
{
    int i, num;
    int ret;
    int *gpios;
    int *values;
    char name[20];
    struct private_data *p = kmalloc(sizeof(struct private_data), GFP_KERNEL);
    dev_err(&pdev->dev, "in probe func\n");
    if (!p) {
        dev_err(&pdev->dev, "allocate memory failed\n");
        ret = -ENOMEM;
        goto KMALLOC_FAILED;
    }

    ret = of_gpio_count(pdev->dev.of_node);
    if (ret < 0) {
        dev_err(&pdev->dev, "read gpios count failed.ret=%d\n", ret);
        goto OF_COUNT_FAILED;
    }
    p->gpios_num = ret;

    gpios = kmalloc(sizeof(int) * p->gpios_num, GFP_KERNEL);
    if (!gpios) {
        dev_err(&pdev->dev, "allocate gpios failed\n");
        goto OF_COUNT_FAILED;
    }
    p->gpios = gpios;

    values = kmalloc(sizeof(int) * p->gpios_num, GFP_KERNEL);
    if (!values) {
        dev_err(&pdev->dev, "allocate gpio values failed\n");
        goto KMALLOC_VALUES_FAILED;
    }
    p->values = values;
    
    for (i = 0; i < p->gpios_num; ++i) {
        gpios[i] = of_get_gpio(pdev->dev.of_node, i);
        values[i] = 0;
        memset(name, 0, 20);
        sprintf(name, "gpio-%d", i);
        ret = gpio_request(gpios[i], name);
        if (ret) {
            dev_err(&pdev->dev, "gpio request %d failed.ret=%d\n", i, ret);
            num = i;
            goto GPIO_REQUEST_FAILED;
        }
        gpio_direction_output(gpios[i], values[i]);
    }
    p->pdev = pdev;
    timer_setup(&p->timer, rk3588_gpio_test_timer_callback, 0);
    add_timer(&p->timer);
    platform_set_drvdata(pdev, p);
    return 0;
GPIO_REQUEST_FAILED:
    for (i = num; i >= 0; --i) {
        gpio_free(gpios[i]);
    }
    kfree(p->values);
KMALLOC_VALUES_FAILED:
    kfree(p->gpios);
OF_COUNT_FAILED:
    kfree(p);
KMALLOC_FAILED:
    return ret;
}

static int rk3588_gpio_test_remove(struct platform_device *pdev)
{
    int i;
    struct private_data *p = platform_get_drvdata(pdev);
    dev_err(&pdev->dev, "in remove func\n");
    del_timer(&p->timer);
    for (i = 0; i < p->gpios_num; ++i) {
        gpio_free(p->gpios[i]);
    }
    kfree(p->values);
    kfree(p->gpios);
    kfree(p);
    return 0;
}

static struct of_device_id match_table[] = {
    {.compatible = "rk3588-gpio-test,guoerba"},
};

static struct platform_driver rk3588_gpio_test_driver = {
    .probe = rk3588_gpio_test_probe,
    .remove = rk3588_gpio_test_remove,
    .driver = {
        .name = "rk3588-gpio-test-driver",
        .of_match_table = match_table,
    }
};

static int __init rk3588_gpio_test_init(void)
{
    if (platform_driver_register(&rk3588_gpio_test_driver)) {
        printk("[%s][%d]register platform driver failed\n", __func__, __LINE__);
        return -EBUSY;
    }
    return 0;
}

static void __exit rk3588_gpio_test_exit(void)
{
    platform_driver_unregister(&rk3588_gpio_test_driver);
}

module_init(rk3588_gpio_test_init);
module_exit(rk3588_gpio_test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yujie Guo");
MODULE_DESCRIPTION("rk3588-gpio-input driver for Learning");



